home *** CD-ROM | disk | FTP | other *** search
/ Programming a Multiplayer FPS in DirectX / Programming a Multiplayer FPS in DirectX (Companion CD).iso / Source / Chapter 6 / Engine / Font.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2004-02-16  |  8.5 KB  |  259 lines

  1. //-----------------------------------------------------------------------------
  2. // File: Font.cpp
  3. //
  4. // Description: Font.h implementation.
  5. //              Refer to the Font.h interface for more details.
  6. //
  7. // Copyright (c) 2004 Vaughan Young
  8. //-----------------------------------------------------------------------------
  9. #include "Engine.h"
  10.  
  11. //-----------------------------------------------------------------------------
  12. // The font class constructor
  13. //-----------------------------------------------------------------------------
  14. Font::Font( char *name, short size, unsigned long bold, bool italic )
  15. {
  16.     HBITMAP bitmap = NULL;
  17.     HGDIOBJ oldBitmap = NULL;
  18.     HFONT oldFont = NULL;
  19.     BYTE *dstRow = NULL;
  20.     unsigned long x, y;
  21.  
  22.     HDC hDC = CreateCompatibleDC( NULL );
  23.     SetMapMode( hDC, MM_TEXT );
  24.  
  25.     // Create the font.
  26.     char height = -MulDiv( size, GetDeviceCaps( hDC, LOGPIXELSY ), 72 );
  27.     HFONT font = CreateFont( height, 0, 0, 0, bold, italic, false, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY, VARIABLE_PITCH, name );
  28.     if( font == NULL )
  29.         goto End;
  30.  
  31.     oldFont = (HFONT)SelectObject( hDC, font );
  32.  
  33.     // Find the dimensions of the smallest texture to hold the characters.
  34.     m_textureWidth = m_textureHeight = 128;
  35.     while( !PrepareFont( hDC, true ) )
  36.     {
  37.         m_textureWidth *= 2;
  38.         m_textureHeight *= 2;
  39.     }
  40.  
  41.     // Create a new texture for the font
  42.     if( FAILED( g_engine->GetDevice()->CreateTexture( m_textureWidth, m_textureHeight, 1, 0, D3DFMT_A4R4G4B4, D3DPOOL_MANAGED, &m_texture, NULL ) ) )
  43.         goto End;
  44.  
  45.     // Prepare the bitmap.
  46.     unsigned long *bitmapBits;
  47.     BITMAPINFO bmi;
  48.     ZeroMemory( &bmi.bmiHeader, sizeof( BITMAPINFOHEADER ) );
  49.     bmi.bmiHeader.biSize = sizeof( BITMAPINFOHEADER );
  50.     bmi.bmiHeader.biWidth = (int)m_textureWidth;
  51.     bmi.bmiHeader.biHeight = -(int)m_textureHeight;
  52.     bmi.bmiHeader.biPlanes = 1;
  53.     bmi.bmiHeader.biCompression = BI_RGB;
  54.     bmi.bmiHeader.biBitCount = 32;
  55.  
  56.     // Create a bitmap for the font.
  57.     bitmap = CreateDIBSection( hDC, &bmi, DIB_RGB_COLORS, (void**)&bitmapBits, NULL, 0 );
  58.  
  59.     oldBitmap = SelectObject( hDC, bitmap );
  60.  
  61.     // Set the text properties.
  62.     SetTextColor( hDC, RGB( 255,255,255 ) );
  63.     SetBkColor( hDC, 0x00000000 );
  64.     SetTextAlign( hDC, TA_TOP );
  65.  
  66.     // Prepare the font by drawing the characters onto the bitmap.
  67.     if( !PrepareFont( hDC ) )
  68.         goto End;
  69.  
  70.     // Lock the surface and write the alpha values for the set pixels.
  71.     D3DLOCKED_RECT d3dlr;
  72.     m_texture->LockRect( 0, &d3dlr, 0, 0 );
  73.     dstRow = (BYTE*)d3dlr.pBits;
  74.     WORD *dst16;
  75.     BYTE alpha;
  76.  
  77.     for( y = 0; y < m_textureHeight; y++ )
  78.     {
  79.         dst16 = (WORD*)dstRow;
  80.         for( x = 0; x < m_textureWidth; x++ )
  81.         {
  82.             alpha = (BYTE)( ( bitmapBits[m_textureWidth*y + x] & 0xff ) >> 4 );
  83.             if( alpha > 0 )
  84.                 *dst16++ = (WORD)( ( alpha << 12 ) | 0x0fff );
  85.             else
  86.                 *dst16++ = 0x0000;
  87.         }
  88.         dstRow += d3dlr.Pitch;
  89.     }
  90.  
  91.     // Create the vertex buffer for the characters.
  92.     g_engine->GetDevice()->CreateVertexBuffer( 1020 * sizeof( TLVertex ), D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC, 0, D3DPOOL_DEFAULT, &m_vb, NULL );
  93.  
  94.     // Prepare the alpha testing for rendering the characters.
  95.     g_engine->GetDevice()->SetRenderState( D3DRS_ALPHAREF, 0x08 );
  96.     g_engine->GetDevice()->SetRenderState( D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL );
  97.  
  98.     // Create a state block for capturing the volatile render states.
  99.     g_engine->GetDevice()->BeginStateBlock();
  100.     g_engine->GetDevice()->SetRenderState( D3DRS_LIGHTING, false );
  101.     g_engine->GetDevice()->SetRenderState( D3DRS_ALPHATESTENABLE, false );
  102.     g_engine->GetDevice()->SetRenderState( D3DRS_FOGENABLE, false );
  103.     g_engine->GetDevice()->EndStateBlock( &m_states );
  104.  
  105.     // Clean up and return.
  106. End:
  107.     if( m_texture )
  108.         m_texture->UnlockRect( 0 );
  109.  
  110.     SelectObject( hDC, oldBitmap );
  111.     SelectObject( hDC, oldFont );
  112.     DeleteObject( bitmap );
  113.     DeleteObject( font );
  114.     DeleteDC( hDC );
  115. }
  116.  
  117. //-----------------------------------------------------------------------------
  118. // The font class destructor
  119. //-----------------------------------------------------------------------------
  120. Font::~Font()
  121. {
  122.     SAFE_RELEASE( m_states );
  123.     SAFE_RELEASE( m_vb );
  124.     SAFE_RELEASE( m_texture );
  125. }
  126.  
  127. //-----------------------------------------------------------------------------
  128. // Prepares the font by drawing all the characters on the device context.
  129. //-----------------------------------------------------------------------------
  130. bool Font::PrepareFont( HDC hDC, bool measure )
  131. {
  132.     SIZE size;
  133.     char string[2];
  134.  
  135.     // Find the spacing between the characters based on the line height.
  136.     if( GetTextExtentPoint32( hDC, string, 1, &size ) == 0 )
  137.         return false;
  138.     m_spacing = (short)ceil( size.cy * 0.1f );
  139.  
  140.     // Set the position to start drawing at.
  141.     unsigned long x = m_spacing;
  142.     unsigned long y = 0;
  143.  
  144.     // Draw each character on the DC and move to the next position.
  145.     for( char c = 32; c < 127; c++ )
  146.     {
  147.         string[0] = c;
  148.         if( GetTextExtentPoint32( hDC, string, 1, &size ) == 0 )
  149.             return false;
  150.  
  151.         if( (unsigned long)( x + size.cx + m_spacing ) > m_textureWidth )
  152.         {
  153.             x = m_spacing;
  154.             y += size.cy + 1;
  155.         }
  156.  
  157.         // Ensure there is enough room to draw the character.
  158.         if( y + size.cy > m_textureHeight )
  159.             return false;
  160.  
  161.         // Draw the character if not measuring.
  162.         if( !measure )
  163.         {
  164.             if( ExtTextOut( hDC, x + 0, y + 0, ETO_OPAQUE, NULL, string, 1, NULL ) == 0 )
  165.                 return false;
  166.  
  167.             m_textureCoords[c - 32][0] = (float)( x - m_spacing ) / m_textureWidth;
  168.             m_textureCoords[c - 32][1] = (float)( y ) / m_textureHeight;
  169.             m_textureCoords[c - 32][2] = (float)( x + size.cx + m_spacing ) / m_textureWidth;
  170.             m_textureCoords[c - 32][3] = (float)( y + size.cy ) / m_textureHeight;
  171.         }
  172.  
  173.         x += size.cx + ( 2 * m_spacing );
  174.     }
  175.  
  176.     return true;
  177. }
  178.  
  179. //-----------------------------------------------------------------------------
  180. // Renders the given text to the screen using this font.
  181. //-----------------------------------------------------------------------------
  182. void Font::Render( char *text, float x, float y, D3DCOLOR colour )
  183. {
  184.     // Capture the current volatile render states.
  185.     m_states->Capture();
  186.  
  187.     // Set the volatile render states.
  188.     g_engine->GetDevice()->SetRenderState( D3DRS_LIGHTING, false );
  189.     g_engine->GetDevice()->SetRenderState( D3DRS_ALPHATESTENABLE, true );
  190.     g_engine->GetDevice()->SetRenderState( D3DRS_FOGENABLE, false );
  191.  
  192.     // Set the non-volatile render states.
  193.     g_engine->GetDevice()->SetTexture( 0, m_texture );
  194.     g_engine->GetDevice()->SetFVF( TL_VERTEX_FVF );
  195.     g_engine->GetDevice()->SetStreamSource( 0, m_vb, 0, TL_VERTEX_FVF_SIZE );
  196.  
  197.     // Adjust for character spacing.
  198.     x -= m_spacing;
  199.     float startX = x;
  200.  
  201.     // Fill the vertex buffer.
  202.     TLVertex* vertices = NULL;
  203.     unsigned long totalTriangles = 0;
  204.     m_vb->Lock( 0, 0, (void**)&vertices, D3DLOCK_DISCARD );
  205.  
  206.     // For each letter in the text, add a textured quad to the vertex buffer.
  207.     while( *text )
  208.     {
  209.         char c = *text++;
  210.  
  211.         if( c == _T( '\n' ) )
  212.         {
  213.             x = startX;
  214.             y += ( m_textureCoords[0][3] - m_textureCoords[0][1] ) * m_textureHeight;
  215.         }
  216.  
  217.         if( ( c - 32 ) < 0 || ( c - 32 ) >= 96 )
  218.             continue;
  219.  
  220.         float tx1 = m_textureCoords[c - 32][0];
  221.         float ty1 = m_textureCoords[c - 32][1];
  222.         float tx2 = m_textureCoords[c - 32][2];
  223.         float ty2 = m_textureCoords[c - 32][3];
  224.  
  225.         float w = ( tx2 - tx1 ) * m_textureWidth;
  226.         float h = ( ty2 - ty1 ) * m_textureHeight;
  227.  
  228.         if( c != _T( ' ' ) )
  229.         {
  230.             *vertices++ = TLVertex( D3DXVECTOR4( x - 0.5f, y + h - 0.5f, 0.0f, 1.0f ), colour, tx1, ty2 );
  231.             *vertices++ = TLVertex( D3DXVECTOR4( x - 0.5f, y - 0.5f, 0.0f, 1.0f ), colour, tx1, ty1 );
  232.             *vertices++ = TLVertex( D3DXVECTOR4( x + w - 0.5f, y + h - 0.5f, 0.0f, 1.0f ), colour, tx2, ty2 );
  233.             *vertices++ = TLVertex( D3DXVECTOR4( x + w - 0.5f, y - 0.5f, 0.0f, 1.0f ), colour, tx2, ty1 );
  234.             *vertices++ = TLVertex( D3DXVECTOR4( x + w - 0.5f, y + h - 0.5f, 0.0f, 1.0f ), colour, tx2, ty2 );
  235.             *vertices++ = TLVertex( D3DXVECTOR4( x - 0.5f, y - 0.5f, 0.0f, 1.0f ), colour, tx1, ty1 );
  236.             totalTriangles += 2;
  237.  
  238.             if( totalTriangles == 340 )
  239.             {
  240.                 // Unlock, render, and relock the vertex buffer.
  241.                 m_vb->Unlock();
  242.                 g_engine->GetDevice()->DrawPrimitive( D3DPT_TRIANGLELIST, 0, totalTriangles );
  243.                 vertices = NULL;
  244.                 m_vb->Lock( 0, 0, (void**)&vertices, D3DLOCK_DISCARD );
  245.                 totalTriangles = 0;
  246.             }
  247.         }
  248.  
  249.         x += w - ( 2 * m_spacing );
  250.     }
  251.  
  252.     // Unlock and render the vertex buffer.
  253.     m_vb->Unlock();
  254.     if( totalTriangles > 0 )
  255.         g_engine->GetDevice()->DrawPrimitive( D3DPT_TRIANGLELIST, 0, totalTriangles );
  256.  
  257.     // Restore the volatile render states.
  258.     m_states->Apply();
  259. }